#ifndef __REG_H__
#define __REG_H__

#include "common.h"

#include "../../lib-common/x86-inc/cpu.h"

enum { R_EAX, R_ECX, R_EDX, R_EBX, R_ESP, R_EBP, R_ESI, R_EDI };
enum { R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI };
enum { R_AL, R_CL, R_DL, R_BL, R_AH, R_CH, R_DH, R_BH };
enum { R_ES, R_CS, R_SS, R_DS };
/* TODO: Re-organize the `CPU_state' structure to match the register
 * encoding scheme in i386 instruction format. For example, if we
 * access cpu.gpr[3]._16, we will get the `bx' register; if we access
 * cpu.gpr[1]._8[1], we will get the 'ch' register. Hint: Use `union'.
 * For more details about the register encoding scheme, see i386 manual.
 */

/* the structure of segment register */
typedef struct {
	uint32_t base;        
	uint32_t limit;       
	uint16_t selector;   
	uint16_t privilege;
} Segment_reg;

/* the structure of segement descriptor */
typedef struct {
	union {
		struct {
			uint16_t limit1 : 16;
			uint16_t base1  : 16;
		};
		uint32_t val1;
	} part1;
	union {
		struct {
			uint8_t base2 	: 8; // 15-8
			uint8_t a 		: 1; // accessed
			uint8_t type 	: 3;
			uint8_t b 		: 1; 
			uint8_t dpl 	: 2; // descriptor privilege level
			uint8_t p 		: 1; // segment present
			uint8_t limit2 	: 4;
			uint8_t avl 	: 1; // available for use by system software
			uint8_t o 		: 1;
			uint8_t x 		: 1;
			uint8_t g 		: 1; // granularity
			uint8_t base3 	: 8;
		};
		uint32_t val2;
	} part2;
} Segment_des;

typedef struct {
     union{
        union {
		uint32_t _32;
		uint16_t _16;
		uint8_t _8[2];
	} gpr[8];

	/* Do NOT change the order of the GPRs' definitions. */

	struct{
            uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi;
			
        };
     };

    swaddr_t eip;

     union {
		struct {
			uint32_t CF		:1;
			uint32_t pad0	:1;
			uint32_t PF		:1;
			uint32_t pad1	:1;
			uint32_t AF		:1;
			uint32_t pad2	:1;
			uint32_t ZF		:1;
			uint32_t SF		:1;
			uint32_t TF		:1;
			uint32_t IF		:1;
			uint32_t DF		:1;
			uint32_t OF		:1;
			uint32_t IOPL	:2;
			uint32_t NT		:1;
			uint32_t pad3	:1;
			uint16_t pad4;
		};
		uint32_t val;
	} eflags;

	struct GDTR{
		uint16_t limit;
		uint32_t base;
	} gdtr;
	
	CR0 cr0;
	
	CR3 cr3;
	Segment_reg reg[4];

} CPU_state;

CPU_state cpu;
Segment_des* Seg_des;

static inline int check_reg_index(int index) {
	assert(index >= 0 && index < 8);
	return index;
}

#define reg_l(index) (cpu.gpr[check_reg_index(index)]._32)
#define reg_w(index) (cpu.gpr[check_reg_index(index)]._16)
#define reg_b(index) (cpu.gpr[check_reg_index(index) & 0x3]._8[index >> 2])

extern const char* regsl[];
extern const char* regsw[];
extern const char* regsb[];

#endif
